package main_example

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// 控制并发有两种经典的方式，一种是WaitGroup，另外一种就是Context

func RunWatch() {

	ctx, cancel := context.WithCancel(context.Background())

	go watch(ctx, "【监控1】")
	go watch(ctx, "【监控2】")
	go watch(ctx, "【监控3】")

	time.Sleep(5 * time.Second)
	fmt.Println("\n【可以了】通知监控停止\n")
	cancel()

	// 为了检测监控过是否停止，如果没有监控输出，就表示停止了
	time.Sleep(5 * time.Second)
}

func watch(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name, "监控退出，停止了...")
			return
		default:
			fmt.Println(name, "goroutine监控中...")
			time.Sleep(1 * time.Second)
		}
	}
}

// 简单例子
func SimpleContext() {

	// context.Background() 返回一个空的 Context，
	// 这个空的 Context 一般用于整个 Context 树的根节点。
	// 使用 context.WithCancel(parent) 函数，
	// 创建一个可取消的子 Context
	ctx, cancel := context.WithCancel(context.Background())
	go func(ctx context.Context) {
		for {
			select {

			// 检测是否有取消通知(有意味着parent context已经发起了取消请求)
			// 如果接受到值的话，就可以返回结束 goroutine 了
			case <-ctx.Done():
				fmt.Println("监控退出，停止了...")
				return

			default:
				fmt.Println("goroutine监控中...")
				time.Sleep(1 * time.Second)
			}
		}
	}(ctx)

	time.Sleep(3 * time.Second)
	fmt.Println("可以了，通知监控停止")
	cancel() // 发起取消的子 Context 请求

	// 为了检测监控过是否停止，如果没有监控输出，就表示停止了
	time.Sleep(3 * time.Second)
}

// WaitGroup
func SyncWaitGroup() {

	// 创建任务组
	wg := sync.WaitGroup{}
	// 添加任务数(设置等待任务数)
	wg.Add(2)

	for i := 0; i < 2; i++ {

		go func() {

			fmt.Println("完成")

			// 每完成一个任务，删除一个（标记一个）
			wg.Done()
		}()

	}

	// 等待所有任务数都完成
	wg.Wait()
	fmt.Println("所有任务数都完成")
}
